﻿@*******************************************************************************************************
//  PagedViewModel.cshtml - Gbtc
//
//  Copyright © 2016, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
//  file except in compliance with the License. You may obtain a copy of the License at:
//
//      http://opensource.org/licenses/MIT
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  01/22/2016 - J. Ritchie Carroll
//       Generated original version of source code.
//
//*****************************************************************************************************@
@using System.Text
@using GSF
@using GSF.Web
@using GSF.Web.Model
@using PQDashboard.Model
@model AppModel
@{
    if (ViewBag.PageControlScripts == null) {
        ViewBag.PageControlScripts = new StringBuilder();
    }

    StringBuilder pageControlScripts = ViewBag.PageControlScripts;

    // Setup page control scripts
    pageControlScripts.Append(Scripts.Render("~/js.bundle/knockout"));
    // TODO: Currently the bundler will fail to properly compress paged view model, check again after updates...
    pageControlScripts.Append($"<script src=\"{Url.Content("~/Scripts/pagedViewModel.js")}\"></script>");
    pageControlScripts.Append(Scripts.Render("~/js.bundle/gsfwebprimeui"));

    pageControlScripts.Append(@"
    <script id=""validationMessageTemplate"" type=""text/html""><!--
        --><em class=""validationPrompt"" data-bind=""validationMessage: field""></em>
    </script>
    <script>
        $(function () {
            $(""#clearSortOrder"").click(function () {
                viewModel.updateSortOrder(viewModel.defaultSortField, viewModel.defaultSortAscending);
            });
    ");

    // Check for optional page behaviors

    // Hide the add new button
    bool hideAddNewButton = ViewBag.HideAddNewButton ?? false;

    // Hide unauthorized controls as disallowed by Edit/AddNew/Delete roles - otherwise unauthorized controls will be disabled
    bool hideUnauthorizedControls = ViewBag.HideUnauthorizedControls ?? false;

    // Auto pop-up add new dialog
    bool autoShowAddNew = ViewBag.RouteID?.Equals("AddNew", StringComparison.OrdinalIgnoreCase) ?? false;

    bool showIsDeletedToggleLink = false;
    bool showingDeletedRecords = false;

    // Check for existence of ShowDeleted property to control toggle link visibility
    if (ViewBag.ShowDeleted != null) {
        showIsDeletedToggleLink = true;
        showingDeletedRecords = Convert.ToBoolean(ViewBag.ShowDeleted);
    }

    // Get current allowed create / update / delete states for user - default to none.
    // Note that overriding view bag settings will only affect UI, back end security
    // will still be applicable
    bool canEdit = ViewBag.CanEdit ?? false;
    bool canAddNew = ViewBag.CanAddNew ?? false;
    bool canDelete = ViewBag.CanDelete ?? false;
}
@functions
{
    string SingularForm(string value)
    {
        if (value.EndsWith("ies", StringComparison.Ordinal))
            return value.Substring(0, value.Length - 3) + "y";

        if (value.EndsWith("IES", StringComparison.Ordinal))
            return value.Substring(0, value.Length - 3) + "Y";

        if (value.EndsWith("ces", StringComparison.OrdinalIgnoreCase))
            return value.Substring(0, value.Length - 1);

        if (value.EndsWith("es", StringComparison.OrdinalIgnoreCase))
            return value.Substring(0, value.Length - 2);

        if (value.EndsWith("s", StringComparison.OrdinalIgnoreCase))
            return value.Substring(0, value.Length - 1);

        return value;
    }

    string RemoveSpaces(string value)
    {
        return value.RemoveWhiteSpace().RemoveInvalidFileNameCharacters();
    }

    string RenderSortScript(string fieldName, bool ascending)
    {
        const string ScriptFormat = @"
            $(""#sort{0} #{1}"").click(function () {{
                viewModel.updateSortOrder(""{0}"", {2});
            }});
        ";

        return string.Format(ScriptFormat, fieldName, ascending ? "asc" : "desc", ascending.ToString().ToLower());
    }
}
@helper WriteFieldHeader(StringBuilder pageControlScripts, string fieldName, string labelName = null, string classes = null)
{
    if (string.IsNullOrEmpty(fieldName))
    {
        <th class="@Html.Raw(classes ?? "text-center")" nowrap>@Html.Raw(labelName ?? "")</th>
    }
    else
    {
        <th class="@Html.Raw(classes ?? "text-center")" id="sort@(fieldName)" nowrap>@Html.Raw(labelName ?? fieldName)&nbsp;
            <div class="btn-group-vertical btn-group-sort">
                <button type="button" class="btn" id="asc" title="Sort&nbsp;ascending..." data-bind="css: {'btn-primary': isSortOrder('@fieldName', true)}, enable: dataHubIsConnected"><span class="glyphicon glyphicon-chevron-up"></span></button>
                <button type="button" class="btn" id="desc" title="Sort&nbsp;descending..." data-bind="css: {'btn-primary': isSortOrder('@fieldName', false)}, enable: dataHubIsConnected"><span class="glyphicon glyphicon-chevron-down"></span></button>
            </div>
        </th>

        pageControlScripts.Append(RenderSortScript(fieldName, true));
        pageControlScripts.Append(RenderSortScript(fieldName, false));
    }
}
<div class="well well-dynamic-content" id="contentWell" content-fill-height>
    <div class="table-responsive" id="responsiveTableDiv">
        <table class="table table-condensed table-hover" id="recordsTable">
            <thead>
                <tr>                
                    @foreach (string[] headerColumn in ViewBag.HeaderColumns) {
                        @WriteFieldHeader(pageControlScripts, headerColumn[0], headerColumn[1], headerColumn[2])
                    }
                    <th class="text-center">
                        <button type="button" class="btn btn-link btn-xs" style="line-height: 1.1" id="clearSortOrder" data-bind="enable: dataHubIsConnected">Clear<br/>Sort</button>
                    </th>
                </tr>
            </thead>
            <tbody data-bind="foreach: pageRecords">
                <tr style="visibility: hidden" id="recordRow"@Html.Raw(ViewBag.ShowDeleted ?? false ? $" data-bind=\"css: {{'danger': {ViewBag.IsDeletedField}}}\"" : "")>
                    @Html.Raw(ViewBag.BodyRows)
                </tr>
            </tbody>
        </table>
        <div id="loadingDataLabel">
            Loading&nbsp;&nbsp;<span class="glyphicon glyphicon-refresh glyphicon-spin"></span>
        </div>
    </div>
    <div class="pull-right" id="pageControlsRow">
        @if (!hideAddNewButton && (canAddNew || !hideUnauthorizedControls)) {
            <button type="button" class="btn btn-sm btn-primary pull-right" id="addRecordButton" @Html.Raw(canAddNew ? "data-bind=\"enable: dataHubIsConnected\"" : "disabled")>
                <span class="glyphicon glyphicon-plus"></span>&nbsp;&nbsp;Add&nbsp;New
            </button>
            <br />
            <hr class="half-break" />
        }
        <div class="btn-group btn-group-sm">
            <button type="button" class="btn btn-default" id="firstPageButton" data-bind="css: {'disabled': onFirstPage()}, enable: dataHubIsConnected() && !onFirstPage()"><span class="glyphicon glyphicon-backward"></span></button>
            <button type="button" class="btn btn-default" id="previousPageButton" data-bind="css: {'disabled': onFirstPage()}, enable: dataHubIsConnected() && !onFirstPage()"><span class="glyphicon glyphicon-triangle-left"></span></button>
        </div>
        <input type="number" class="content input-sm" style="padding: 0 0 0 5px; width: 55px" id="selectedPage" data-bind="numeric, textInput: currentPage, enable: dataHubIsConnected" value="1">
        &nbsp;of&nbsp;
        <span data-bind="text: totalPages">1</span>
        &nbsp;
        <div class="btn-group btn-group-sm">
            <button type="button" class="btn btn-default" id="nextPageButton" data-bind="css: {'disabled': onLastPage()}, enable: dataHubIsConnected() && !onLastPage()"><span class="glyphicon glyphicon-triangle-right"></span></button>
            <button type="button" class="btn btn-default" id="lastPageButton" data-bind="css: {'disabled': onLastPage()}, enable: dataHubIsConnected() && !onLastPage()"><span class="glyphicon glyphicon-forward"></span></button>
        </div>
    </div>
</div>
<div id="addNewEditDialog" class="modal fade" role="dialog">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header" data-bind="css: {'modal-readonly': recordMode()===RecordMode.View}">
                <button type="button" class="close" id="dismissDialogButton">&times;</button>
                <h4 class="modal-title">
                    <span data-bind="visible: recordMode()===RecordMode.View">View</span>
                    <span data-bind="visible: recordMode()===RecordMode.Edit">Edit</span>
                    <span data-bind="visible: recordMode()===RecordMode.AddNew">Add New</span>
                    @(ViewBag.AddNewEditTitle ?? SingularForm(ViewBag.Title ?? ""))
                </h4>
            </div>
            <div class="modal-body auto-height" data-bind="with: currentRecord, validationOptions: {messageTemplate: 'validationMessageTemplate'}, css: {'modal-readonly': recordMode()===RecordMode.View}">
                <form role="form">
                    @Html.Raw(ViewBag.AddNewEditDialog)
                </form>
            </div>
            <div class="modal-footer" data-bind="css: {'modal-readonly': recordMode()===RecordMode.View}">
                <em data-bind="visible: unassignedFieldCount() > 0 || validationErrors() > 0">
                    <span data-bind="visible: unassignedFieldCount() > 0">Missing <span data-bind="text: unassignedFieldCount"></span> required field<span data-bind="visible: unassignedFieldCount() > 1">s</span></span>
                    <span data-bind="visible: unassignedFieldCount() > 0 && validationErrors() > 0"> and </span>
                    <span data-bind="visible: validationErrors() > 0"><span data-bind="text: validationErrors()"></span> validation error<span data-bind="visible: validationErrors() > 1">s</span></span>
                    ...
                </em>
                @if (canEdit || !hideUnauthorizedControls) {
                    <button type="button" class="btn btn-info" data-bind="visible: recordMode()===RecordMode.View@(Html.Raw(canEdit ? ", enable: dataHubIsConnected" : ""))" id="editRecordButton"@Html.Raw(canEdit ? "" : " disabled")>Edit</button>
                }
                @if (canEdit || canAddNew || !hideUnauthorizedControls) {
                    <button type="submit" class="btn btn-primary" data-dismiss="modal" data-bind="visible: recordMode()!==RecordMode.View@(Html.Raw(canEdit || canAddNew ? ", disable: unassignedFieldCount() > 0 || validationErrors() > 0 || !dataHubIsConnected()" : ""))" id="saveRecordButton"@Html.Raw(canEdit || canAddNew ? "" : " disabled")>Save</button>
                }
                <button type="button" class="btn btn-default" id="cancelRecordButton">Cancel</button>
            </div>
        </div>
    </div>
</div>
@{
    DataContext dataContext = Model.DataContext;

    if (autoShowAddNew) {
        pageControlScripts.Append(@"
            // Add new pop-up requested via URL parameter
            setTimeout(viewModel.addPageRecord, 500);
        ");
    }

    if (showIsDeletedToggleLink) {
        string baseURL = Request?.Url?.AbsolutePath ?? "/";
        baseURL = baseURL.Substring(0, baseURL.Length - (ViewBag.RouteID ?? "").Length);

        if (showingDeletedRecords) {
            pageControlScripts.Append($@"
            $(""#titleText"").append(""<br /><div style='margin-top: 3px' class='small text-center'><a href='{baseURL}'>Hide Deleted</a></div>"");
        ");
        }
        else {
            pageControlScripts.Append($@"
            $(""#titleText"").append(""<br /><div style='margin-top: 3px' class='small text-center'><a href='{baseURL.EnsureEnd('/')}ShowDeleted'>Show Deleted</a></div>"");
        ");
        }
    }

    pageControlScripts.Append(@"
            $(""#addRecordButton"").click(function () {
                viewModel.addPageRecord();
            });

            $(""#editRecordButton"").click(function () {
                viewModel.recordMode(RecordMode.Edit);
            });

            $(""#saveRecordButton"").click(function () {
                viewModel.savePageRecord();
            });

            $(""#cancelRecordButton"").click(function () {
                viewModel.cancelPageRecord();
            });

            $(""#dismissDialogButton"").click(function () {
                viewModel.cancelPageRecord();
            });

            $(""#addNewEditDialog"").modal({show: false, backdrop: ""static"", keyboard: false});

            $(window).on(""beforeunload"", function () {
                if ($(""#addNewEditDialog"").hasClass(""in"") && viewModel.isDirty())
                    return ""There are unsaved changes to this record."";

                return undefined;
            });
        });
    ");

    pageControlScripts.AppendFormat(@"
        viewModel.canEdit({0});
        viewModel.canAddNew({1});
        viewModel.canDelete({2});

        viewModel.initialFocusField = ""{3}"";
        viewModel.modelName = ""{4}"";",
        canEdit.ToString().ToLower(),
        canAddNew.ToString().ToLower(),
        canDelete.ToString().ToLower(),
        dataContext.InitialFocusField,
        RemoveSpaces(ViewBag.Title));

    if (dataContext.FieldValueInitializers.Count > 0) {
        pageControlScripts.AppendFormat(@"

        $(viewModel).on(""newRecord"", function(event, newRecord) {{
            // Set initial field values");

        foreach (Tuple<string, string> initialValue in dataContext.FieldValueInitializers) {
            pageControlScripts.AppendFormat("\r\n            newRecord.{0} = {1};",
                initialValue.Item1, initialValue.Item2);
        }

        pageControlScripts.Append("\r\n        });");
    }

    if (dataContext.DefinedDateFields.Count > 0) {
        pageControlScripts.AppendFormat(@"

        $(viewModel).on(""beforeSave"", function(event, observableRecord) {{
            // Convert date objects to strings");

        foreach (string dateField in dataContext.DefinedDateFields) {
            pageControlScripts.AppendFormat("\r\n            observableRecord.{0}(notNull(observableRecord.{0}()).formatDate(DateFormat));", dateField);
        }

        pageControlScripts.Append("\r\n        });");

        pageControlScripts.AppendFormat(@"

        $(viewModel).on(""recordSaved"", function(event, observableRecord) {{
            // Convert date strings back to date objects before serialization");

        foreach (string dateField in dataContext.DefinedDateFields) {
            pageControlScripts.AppendFormat("\r\n            observableRecord.{0}(observableRecord.{0}().toDate());", dateField);
        }

        pageControlScripts.Append("\r\n        });");
    }

    if (dataContext.FieldValidationParameters.Count > 0) {
        pageControlScripts.Append("\r\n\r\n        viewModel.setApplyValidationParameters(function () {");

        foreach (KeyValuePair<string, Tuple<string, string>> fieldValidation in dataContext.FieldValidationParameters) {
            pageControlScripts.AppendFormat("\r\n            {0}.extend({{ pattern: {{ message: \"{1}\", params: \"{2}\" }} }});",
                fieldValidation.Key,
                fieldValidation.Value.Item2.JavaScriptEncode(),
                fieldValidation.Value.Item1.JavaScriptEncode());
        }

        pageControlScripts.Append("\r\n        });");
    }

    pageControlScripts.Append(@"

        $(""#addNewEditDialog"").on(""shown.bs.modal"", function () {
            $(""#addNewEditDialog .input-group.date"").datepicker({
                todayBtn: ""linked"",
                enableOnReadonly: false,
                autoclose: true
            });");

    if (dataContext.ReadonlyHotLinkFields.Count > 0) {
        foreach (Tuple<string, string, string, bool> readonlyHotLinkField in dataContext.ReadonlyHotLinkFields) {
            pageControlScripts.AppendLine();

            if (readonlyHotLinkField.Item4) {
                pageControlScripts.AppendFormat("\r\n            $(\"#{0}\").after(\"<div id=\\\"{1}\\\" tabindex=\\\"0\\\" class=\\\"form-control readonly textarea\\\" data-bind=\\\"html: renderHotLinks(notNull({2}())), visible: viewModel.recordMode()===RecordMode.View\\\"></div>\");",
                    readonlyHotLinkField.Item1, readonlyHotLinkField.Item2, readonlyHotLinkField.Item3);
                pageControlScripts.AppendFormat("\r\n            $(\"#{0}\").height($(\"#{1}\").height());",
                    readonlyHotLinkField.Item2, readonlyHotLinkField.Item1);
            }
            else {
                pageControlScripts.AppendFormat("\r\n            $(\"#{0}\").after(\"<div id=\\\"{1}\\\" tabindex=\\\"0\\\" class=\\\"form-control readonly inputtext\\\" data-bind=\\\"html: renderHotLinks(notNull({2}())), visible: viewModel.recordMode()===RecordMode.View\\\"></div>\");",
                    readonlyHotLinkField.Item1, readonlyHotLinkField.Item2, readonlyHotLinkField.Item3);
            }

            pageControlScripts.AppendFormat("\r\n            ko.applyBindings(viewModel.currentRecord, $(\"#{0}\")[0]);",
                readonlyHotLinkField.Item2);
        }
    }

    pageControlScripts.Append("\r\n        });");

    pageControlScripts.Append(@"
    </script>");
}